import wx
import math
import custom_events
import splines
import ProNOIobjects

class DoodleWindow(wx.Window):
    colours = ['Black', 'Yellow', 'Red', 'Green', 'Blue', 'Purple',
        'Brown', 'Aquamarine', 'Forest Green', 'Light Blue', 'Goldenrod',
        'Cyan', 'Orange', 'Navy', 'Dark Grey', 'Light Grey']

    thicknesses = [1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]

    def __init__(self, parent, pic="room.pgm"):
        self.viewGVF = True
        self.viewSnake = True
        self.viewGaussian = False
        self.tubePoints = list()
        self.ellipses = list()
        self.activeObject = -1
        self.drawTool = 0

        self.sizeScale = [1.0,1.0]
        
        self.imgLoc = pic
        self.initsize = wx.Image(self.imgLoc, wx.BITMAP_TYPE_ANY).GetSize()
        super(DoodleWindow, self).__init__(parent,
            style=wx.NO_FULL_REPAINT_ON_RESIZE)
        self.parent = parent
        self.initDrawing()
        self.makeMenu()
        self.bindEvents()
        self.initBuffer()
        
    def initDrawing(self):
        self.SetBackgroundColour('WHITE')
        self.currentThickness = self.thicknesses[0]
        self.currentColour = self.colours[0]
        self.lines = []
        self.previousPosition = (0, 0)
        
    def bindEvents(self):
        for event, handler in [ \
                (wx.EVT_LEFT_DOWN, self.onLeftDown), # Start drawing
                (wx.EVT_LEFT_UP, self.onLeftUp),     # Stop drawing 
                (wx.EVT_MOTION, self.onMotion),      # Draw
                (wx.EVT_SIZE, self.onSize),          # Prepare for redraw
                (wx.EVT_IDLE, self.onIdle),          # Redraw
                (wx.EVT_PAINT, self.onPaint),        # Refresh
                (wx.EVT_WINDOW_DESTROY, self.cleanup)]:
            self.Bind(event, handler)

    def initBuffer(self):
        ''' Initialize the bitmap used for buffering the display. '''
        size = self.GetClientSize()
        bmp = wx.Image(self.imgLoc, wx.BITMAP_TYPE_ANY)
        self.imgSize = bmp.GetSize()
        
        self.sizeScale = [float(size[0]),#/float(self.imgSize[0]), 
                          float(size[1])]#/float(self.imgSize[1])]
        
        img = bmp.Scale(size.width, size.height, wx.IMAGE_QUALITY_HIGH).ConvertToBitmap()
        self.refbuffer = img
        #print size
        self.buffer = img
        dc = wx.BufferedDC(None, self.buffer)
        #pen = wx.Pen(wx.NamedColour('Blue'), 1, wx.SOLID)
        #dc.SetPen(pen)
        if hasattr(self, 'gvf') and self.viewGVF:
            vecs = self.gvf.genQuiver(self.buffer.GetSize())
            dc.BeginDrawing()
            dc.SetPen(wx.Pen(wx.NamedColour('Blue'), 1, wx.SOLID))
            dc.DrawLineList(vecs)
            dc.EndDrawing()
        if hasattr(self, 'lastsnake') and self.viewSnake:
            self.disppts = self.lastsnake.transformCoords(size)
            dc.BeginDrawing()
            dc.SetPen(wx.Pen(wx.NamedColour('Blue'), 1, wx.SOLID))
            #exact line drawing
            #dc.DrawLines(self.lastsnake.getLines(self.disppts))
            #smoothed line drawing using splines
            dc.DrawSpline(self.lastsnake.getLines(self.disppts))
            dc.DrawPointList(self.disppts, wx.Pen(wx.NamedColour('Green'), 4, wx.SOLID))
            dc.EndDrawing()
        if (len(self.ellipses) > 0):
            dc.SetBrush(wx.Brush('Blue', style=wx.TRANSPARENT))
            gc = wx.GraphicsContext.Create(dc)
            if gc:
                ndx = 0
                for ell in self.ellipses:
                    gc.PushState()
                    if (ndx == self.activeObject):
                        gc.SetPen(wx.GREEN_PEN)
                        dc.SetPen(wx.GREEN_PEN)
                    else:
                        gc.SetPen(wx.RED_PEN)
                        dc.SetPen(wx.RED_PEN)
                    tool = ell.objtype
                    if (tool == 0):
                        #move point to ellipse location
                        x = ell.x * self.sizeScale[0]
                        y = ell.y * self.sizeScale[1]
                        a = ell.a * self.sizeScale[0]
                        b = ell.b * self.sizeScale[1]
                        t = ell.theta
                        gc.Translate(x, y)
                        
                        #Rotate GContext to angle specified
                        theta = t
                        gc.Rotate(theta)
                        gp = gc.CreatePath()
                        #Use negative half dimensions of ellipse to force GC to rotate ellipse about midpoint
                        p1 = -a/2.0
                        p2 = -b/2.0
                        
                        gp.AddEllipse(p1,p2, a, b)
                        #TEMP
                        gp.AddRectangle(p1,p2, a, b)
                        
                        #Move rotated ellipse center to the original ellipse center
                        #by rotating coordinates used previously
                        #gm = gc.GetTransform()
                        #gm.Set(math.cos(theta), -math.sin(theta), math.sin(theta), math.cos(theta))
                        #(tp1,tp2) = gm.TransformPoint(-p1,-p2)
                        #gc.Translate(tp1,tp2)
                        
                        gc.StrokePath(gp)
                    #draw ER curve with either a line or a spline depending on the # of points
                    elif (tool == 1):
                        radius = 10.0
                        try:
                            radius = ell.width / float(self.appx)
                        except ValueError:
                            pass
                        for pt in ell.ctrlpts:
                            npt = (pt[0]*self.sizeScale[0], pt[1]*self.sizeScale[1])
                            dc.DrawCirclePoint(npt, radius)
                        for plist in ell.points:
                            #dc.DrawPointList(plist, wx.Pen('Black'))
                            transptlist = ProNOIobjects.ScalePoints(self.sizeScale, plist)
                            dc.DrawLines(transptlist)
                    gc.PopState()
                    ndx += 1
            else:
                #should not be called...
                print 'gc error!'
        self.reInitBuffer = False

    def makeMenu(self):
        ''' Make a menu that can be popped up later. '''
        self.menu = wx.Menu()
        self.idToColourMap = self.addCheckableMenuItems(self.menu,
            self.colours)
#        self.bindMenuEvents(menuHandler=self.onMenuSetColour,
#            updateUIHandler=self.onCheckMenuColours,
#            ids=self.idToColourMap.keys())
        self.menu.Break() # Next menu items go in a new column of the menu
        self.idToThicknessMap = self.addCheckableMenuItems(self.menu,
            self.thicknesses)
#        self.bindMenuEvents(menuHandler=self.onMenuSetThickness,
#            updateUIHandler=self.onCheckMenuThickness,
#            ids=self.idToThicknessMap.keys())

    @staticmethod
    def addCheckableMenuItems(menu, items):
        ''' Add a checkable menu entry to menu for each item in items. This
            method returns a dictionary that maps the menuIds to the
            items. '''
        idToItemMapping = {}
        for item in items:
            menuId = wx.NewId()
            idToItemMapping[menuId] = item
            menu.Append(menuId, str(item), kind=wx.ITEM_CHECK)
        return idToItemMapping

    def bindMenuEvents(self, menuHandler, updateUIHandler, ids):
        ''' Bind the menu id's in the list ids to menuHandler and
            updateUIHandler. '''
        sortedIds = sorted(ids)
        firstId, lastId = sortedIds[0], sortedIds[-1]
        for event, handler in \
                [(wx.EVT_MENU_RANGE, menuHandler),
                 (wx.EVT_UPDATE_UI_RANGE, updateUIHandler)]:
            self.Bind(event, handler, id=firstId, id2=lastId)
    
    def openImage(self, path):
        self.imgLoc = path
        size = wx.Image(self.imgLoc, wx.BITMAP_TYPE_ANY).GetSize()
        self.parent.imgUpdate(size)
        del(self.gvf)
        del(self.lastsnake)
        self.reInitBuffer = True

    # Event handlers:

    def onLeftDown(self, event):
        ''' Called when the left mouse button is pressed. '''
        #self.currentLine = []
        self.previousPosition = event.GetPositionTuple()
        self.CaptureMouse()
        if (self.drawTool == 1):
            ndx = self.activeObject
            #will always need to make up x4, use straight line approximation
            if (ndx != -1):
                ellipse = self.ellipses[ndx]
                elist = self.ellipses[ndx].ctrlpts
                curnumpts = len(elist)
                pt0 = 0
                pt1 = 0
                pt2 = 0
                pt3 = (float(self.previousPosition[0])/self.sizeScale[0], float(self.previousPosition[1])/self.sizeScale[1])
                pt4 = 0
                if (curnumpts > 1):
                    pt1 = elist[-2]
                    pt2 = elist[-1]
                    pt4 = (pt3[0] + (pt3[0] - pt2[0]), pt3[1] + (pt3[1] - pt2[1]))
                    
                    if (curnumpts == 2):
                        #edit last spline to incorporate this new point
                        pt0 = (pt1[0] - (pt2[0] - pt1[0]), pt1[1] - (pt2[1] - pt1[1]))
                    else:
                        pt0 = elist[-3]

                    plist = splines.generateSplinePoints(pt0,pt1,pt2,pt3,0,0,20)
                    ellipse.points.pop()
                    ellipse.points.append(plist)
                        
                    nlist = splines.generateSplinePoints(pt1,pt2,pt3,pt4,0,0,20)
                    ellipse.points.append(nlist)
                elif (curnumpts == 1):
                    pt2 = elist[0]
                    pt1 = (pt2[0] - (pt3[0] - pt2[0]), pt2[1] - (pt3[1] - pt2[1]))
                    pt4 = (pt3[0] + (pt3[0] - pt2[0]), pt3[1] + (pt3[1] - pt2[1]))
                    nlist = splines.generateSplinePoints(pt1,pt2,pt3,pt4,0,0,20)
                    ellipse.points.append(nlist)
                else:
                    ellipse.points.append(list())
                
                ellipse.ctrlpts.append(pt3)
                
            self.reInitBuffer = True

    def onLeftUp(self, event):
        ''' Called when the left mouse button is released. '''
        if self.HasCapture():
            self.currentLine = []
            self.ReleaseMouse()
            """
    def onRightUp(self, event):
        ''' Called when the right mouse button is released, will popup
            the menu. '''
        self.PopupMenu(self.menu)
    """
    def onMotion(self, event):
        ''' Called when the mouse is in motion. If the left button is
            dragging then draw a line from the last event position to the
            current one. Save the coordinants for redraws. '''
        #TODO add ER tube elif to create points as cursor moves whether time based (probably) or distance based
        if event.Dragging() and event.LeftIsDown() and self.activeObject != -1:
            
            dc = wx.BufferedDC(wx.ClientDC(self), self.buffer)
            currentPosition = event.GetPositionTuple()
            
            if (self.drawTool == 0):
                #Ensures ellipse box is non-negative
                a = float(currentPosition[0] - self.previousPosition[0])/self.sizeScale[0]
                b = float(currentPosition[1] - self.previousPosition[1])/self.sizeScale[1]
                x = float(self.previousPosition[0])/self.sizeScale[0]
                y = float(self.previousPosition[1])/self.sizeScale[1]
                if (a < 0):
                    x = a+x
                    a = -a
                if (b < 0):
                    y = b+y
                    b = -b
                    
                e = ProNOIobjects.object.initEllipsoid(x, y, a, b, 0)
                
                #Draws temporary ellipse onto image
                dc.SetBrush(wx.Brush('Blue', style=wx.TRANSPARENT))
                dc.SetPen(wx.Pen('Green'))
                #dc.DrawEllipse(e[0], e[1],e[2],e[3])
                #dc.DrawRectangle(e[0],e[1],e[2],e[3])
                
                evt = custom_events.EllipseDrawEvent(ellipse=e)
                wx.PostEvent(self, evt)
                
                self.ellipses[self.activeObject] = e
                self.initBuffer()

    def onSize(self, event):
        ''' Called when the window is resized. We set a flag so the idle
            handler will resize the buffer. '''
        #self.IsResizeEvent = True
        self.reInitBuffer = True

    def onIdle(self, event):
        ''' If the size was changed then resize the bitmap used for double
            buffering to match the window size.  We do it in Idle time so
            there is only one refresh after resizing is done, not lots while
            it is happening. 
            '''
        if self.reInitBuffer:
            self.initBuffer()
            self.Refresh(False)

    def onPaint(self, event):
        ''' Called when the window is exposed. '''
        # Create a buffered paint DC.  It will create the real
        # wx.PaintDC and then blit the bitmap to it when dc is
        # deleted.  Since we don't need to draw anything else
        # here that's all there is to it.
        dc = wx.BufferedPaintDC(self, self.buffer)
        pen = wx.Pen(wx.NamedColour('Blue'), 1, wx.SOLID)
        dc.SetPen(pen)

    def cleanup(self, event):
        if hasattr(self, "menu"):
            self.menu.Destroy()
            del self.menu
    
    def dispSnake (self, snakes):
        numsnakes = len(snakes)-1
        self.lastsnake = snakes[numsnakes]
        self.lastsnake.setLastSize(self.imgSize)
        self.reInitBuffer = True
        
    def dispGVF (self, gvf):
        self.gvf = gvf
        self.reInitBuffer = True
    def RedrawEllipse(self):
        self.reInitBuffer = True
